"""
Redis队列工具 - 用于存储OCR识别任务
队列的作用：把需要处理的任务存起来，等定时任务慢慢处理

队列工作机制详解：
1. 使用Redis的列表数据结构实现先进先出(FIFO)队列
2. 任务通过rpush命令添加到队列尾部
3. 处理器通过lpop命令从队列头部取出任务
4. 提供队列长度查询功能，便于监控队列状态
"""

# 导入标准库
import redis  # Redis客户端库，用于与Redis服务器通信
import json  # JSON处理库，用于序列化和反序列化任务数据
from typing import Optional, Dict  # 类型提示，提高代码可读性和类型安全性

# 导入项目内部模块
from config import settings  # 应用配置，获取Redis连接参数

# 创建一个Redis连接对象
# Redis连接配置说明：
# host='localhost' - Redis服务器地址，本地开发环境使用localhost
# port=6379 - Redis默认端口号
# db=0 - 使用Redis的第一个数据库（编号从0开始）
# decode_responses=True - 自动将返回的字节数据解码为字符串
redis_client = redis.Redis(
    host='localhost',  # Redis服务器的地址，本地就是localhost
    port=6379,         # Redis的端口号，默认是6379
    db=0,              # 使用第0个数据库
    decode_responses=True  # 自动把返回的数据转换成字符串，方便处理
)

# 定义队列的键名，队列就像一个列表，用这个名字来标识
# 设计说明：使用固定键名确保所有服务实例操作同一个队列
QUEUE_KEY = "ocr_task_queue"


def push_task_to_queue(task_data: Dict) -> bool:
    """
    把任务添加到Redis队列的尾部
    
    队列操作机制：
    1. 将任务数据序列化为JSON字符串
    2. 使用rpush命令将任务添加到队列尾部
    3. 记录操作日志便于调试和监控
    4. 异常处理确保系统稳定性
    
    参数:
        task_data: 任务数据字典，包含用户ID、图片信息等
              典型结构：{'user_id': 123, 'card_side': 'front', 'image_url': '...'}
    
    返回:
        True表示成功，False表示失败
              布尔值用于上层调用判断操作结果
    """
    try:
        # 把任务数据转换成JSON字符串格式
        # JSON就像把字典打包成一个字符串，方便存储和传输
        # dumps函数将Python对象序列化为JSON格式字符串
        task_json = json.dumps(task_data)
        
        # 使用Redis的rpush命令，把任务添加到队列右边（尾部）
        # rpush = right push，从右边推入，就像排队时站在队尾
        # 这确保了任务按照先进先出(FIFO)的顺序被处理
        redis_client.rpush(QUEUE_KEY, task_json)
        
        # 打印日志，方便调试和查看
        # 从任务数据中提取关键信息用于日志记录
        print(f"[队列] 任务已添加: {task_data.get('user_id')} - {task_data.get('card_side')}")
        
        # 返回True表示成功
        return True
        
    except Exception as e:
        # 如果出错了，打印错误信息
        # 异常捕获确保单个任务添加失败不影响整个系统运行
        print(f"[队列错误] 添加任务失败: {e}")
        # 返回False表示失败
        return False


def pop_task_from_queue() -> Optional[Dict]:
    """
    从Redis队列的头部取出一个任务
    
    队列消费机制：
    1. 使用lpop命令原子性地从队列头部取出任务
    2. 将JSON字符串反序列化为Python字典
    3. 记录操作日志便于监控任务处理进度
    4. 处理空队列情况，返回None表示无任务可处理
    
    返回:
        任务数据字典，如果队列为空则返回None
              返回值类型为Optional[Dict]，明确表示可能为空的情况
    """
    try:
        # 使用Redis的lpop命令，从队列左边取出一个任务
        # lpop = left pop，从左边弹出，就像排队时最前面的人先处理
        # 原子性操作确保多个消费者不会取到同一个任务
        task_json = redis_client.lpop(QUEUE_KEY)
        
        # 如果队列是空的，lpop会返回None
        if task_json is None:
            # 没有任务了，返回None
            # 这是正常情况，表示队列中暂无待处理任务
            return None
        
        # 把JSON字符串转换回字典格式
        # json.loads = JSON load string，把字符串解析成字典
        # loads函数将JSON格式字符串反序列化为Python对象
        task_data = json.loads(task_json)
        
        # 打印日志，方便调试
        # 记录被取出的任务信息，便于追踪任务处理流程
        print(f"[队列] 任务已取出: {task_data.get('user_id')} - {task_data.get('card_side')}")
        
        # 返回任务数据
        return task_data
        
    except Exception as e:
        # 如果出错了，打印错误信息
        # 异常处理确保队列服务异常不会影响整个系统稳定性
        print(f"[队列错误] 取出任务失败: {e}")
        # 返回None表示失败
        return None


def get_queue_length() -> int:
    """
    获取队列中还有多少个任务
    
    队列监控机制：
    1. 使用llen命令获取队列长度
    2. 返回整数值表示待处理任务数量
    3. 便于系统监控和负载评估
    4. 为任务调度提供决策依据
    
    返回:
        任务数量
              整数类型，表示队列中待处理的任务总数
    """
    try:
        # 使用llen命令，获取队列的长度（有多少个任务）
        # llen = list length，列表长度
        # 这是一个O(1)时间复杂度的操作，性能高效
        length = redis_client.llen(QUEUE_KEY)
        # 返回任务数量
        return length
    except Exception as e:
        # 出错了打印错误，返回0
        # 异常情况下返回0，表示无法获取队列长度
        print(f"[队列错误] 获取队列长度失败: {e}")
        return 0